/*
	Java FTP Proxy Server 1.1.0
 */

package ftpproxy;

import static ftpproxy.Constants.DEFAULT_BACKLOG;
import static ftpproxy.Constants.pwDebug;

import java.io.FileInputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

public class FtpProxy {
	private final static String defaultConfigFile = "FtpProxy.conf";

	

	// use CRLF instead of println() to ensure that CRLF is used
	// on all platforms

	public FtpProxy(Configuration config, Socket skControlClient) {
		// this.config = config;
		// this.skControlClient = skControlClient;

		// sLocalClientIP is initialized in main(), to handle
		// masquerade_host where the IP address for the host is dynamic.
	}

	public static void main(String args[]) {
		Configuration config = null;
		try {
			Map<String, String> commandLineArguments = extractCommandLineArguments(args);
			config = loadConfigFile(commandLineArguments);
		} catch (RuntimeException re) {
			System.err.println(re.getMessage());
			System.exit(0);
		}
		
		int port = config.bindPort;
		
		try {
			ServerSocket ssControlClient;

			if (config.bindAddress == null) {
				ssControlClient = new ServerSocket(port);
			} else {
				ssControlClient = new ServerSocket(port, DEFAULT_BACKLOG, config.bindAddress);
			}

			if (config.debug) {
				Log.write(config, "Listening on port " + port);
				Log.write(config, "ServerSocket backlog = " + DEFAULT_BACKLOG);
			}

			while (true) {
				Socket skControlClient = ssControlClient.accept();

				if (config.debug) {
					Log.write(config, "New Connection!!!");
					Log.write(config, "Client info : " + skControlClient.toString());
				}

				Thread t = new Thread(new ClientHandler(config, skControlClient));
				t.start();

				// Thread ct = Thread.currentThread();
				// new FtpProxy(config, skControlClient).start();
			}
		} catch (IOException e) {
			if (config.debug) {
				e.printStackTrace(pwDebug);
			} else {
				System.err.println(e.toString());
			}
		} catch (Exception e2) {
			e2.printStackTrace();
		}
	}

	private static Map<String, String> extractCommandLineArguments(String args[]) throws RuntimeException {
    	Map<String, String> commandLineArguments = new HashMap<String, String>(args.length);
    	for (String arg : args) {
    		int j = arg.indexOf("=");
    
    		if (j == -1)
    			throw new RuntimeException("Invalid argument: " + arg);
    
    		String name = arg.substring(0, j);
    		String value = arg.substring(j + 1);
    
    		if (commandLineArguments.containsKey(name))
    			throw new RuntimeException("Parameter error: --" + name + " may only be specified once.");
    
    		commandLineArguments.put(name, value);
    	}
    	
    	return commandLineArguments;
	}

	private static Configuration loadConfigFile(Map<String, String> commandLineArguments) throws RuntimeException {
		String configFile = (String) commandLineArguments.get("config_file");
		if (configFile == null)
			configFile = ClassLoader.getSystemResource(defaultConfigFile).getFile();
		
		Properties properties = new Properties();
		try {
			properties.load(new FileInputStream(configFile));
		} catch (IOException e) {
			throw new RuntimeException("Configuration file error: " + e.getMessage());
		}
		
		// Command Line Aarguments override those in the config file
		properties.putAll(commandLineArguments);
		
    	Configuration config;
    	try {
    		config = new Configuration(properties);
    	} catch (Exception e) {		
    		throw new RuntimeException("Invalid configuration: " + e.getMessage());
    	}
    
    	if (properties.size() > 0) {
    		throw new RuntimeException("Invalid configuration variable: " + properties.propertyNames().nextElement());
    	}

    	return config;
	}
}